package com.sj.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sj.common.bean.LocalUser;
import com.sj.common.dto.dict.ArticleNumDict;
import com.sj.common.dto.user.*;
import com.sj.common.exception.CustomException;
import com.sj.common.lang.ApiResult;
import com.sj.common.lang.Const;
import com.sj.common.lang.RedisConst;
import com.sj.entity.*;
import com.sj.interceptor.AuthCheck;
import com.sj.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.sql.Wrapper;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author 孙峻
 * @since 2022-03-09
 */
@RestController
@RequestMapping("/user")
@Api(tags = "用户模块")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private ArticleService articleService;

    @Autowired
    private CollectService collectService;

    @Autowired
    private FootprintService footprintService;

    @Autowired
    private DictDetailService dictDetailService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PraiseService praiseService;

    @Autowired
    private LabelService labelService;

    @ApiOperation("根据用户id获取用户信息")
    @GetMapping("/findById/{id}")
    public ApiResult getUserInfo(@PathVariable Integer id){
        UserDto userDetailInfo = userService.getUserDetailInfo(id);
        return ApiResult.success(userDetailInfo);
    }

    @DeleteMapping("/admin/del/{id}")
    @ApiOperation("管理端删除用户")
    @AuthCheck
    public ApiResult delUser(@PathVariable Integer id) {
        boolean result = userService.removeById(id);

        if (result) {
            // 删除用户标签
            labelService.remove(Wrappers.<Label>lambdaQuery().eq(Label::getUserId, id));
            // 删除用户文章
            articleService.remove(Wrappers.<Article>lambdaQuery().eq(Article::getUserId, id));
            // 删除用户点赞
            praiseService.remove(Wrappers.<Praise>lambdaQuery().eq(Praise::getUserId, id));
            // 删除用户评论
            commentService.remove(Wrappers.<Comment>lambdaQuery().eq(Comment::getUserId, id));
            // 删除用户关联角色记录
            userRoleService.remove(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, id));
            // 删除用户足迹
            footprintService.remove(Wrappers.<Footprint>lambdaQuery().eq(Footprint::getUserId, id));
            return ApiResult.success("删除成功");
        }
        return ApiResult.error("删除失败");
    }

    @ApiOperation("管理端添加用户")
    @PutMapping("/admin/save")
    @AuthCheck
    public ApiResult addUser(@RequestBody AdminUserSaveParam param) {
        User user = User.builder()
                .id(param.getId())
                .username(param.getUsername()).build();
        if(StrUtil.isNotBlank(param.getPassword())){
            user.setPassword(bCryptPasswordEncoder.encode(param.getPassword()));
        }
        boolean result = false;
        try {
            result = userService.saveOrUpdate(user);
        } catch (Exception e) {
            throw new CustomException("该用户名已存在");
        }
        if (result) {
            // 删除该用户原有角色
            userRoleService.remove(Wrappers.<UserRole>lambdaQuery().eq(UserRole::getUserId, user.getId()));
            // 添加角色信息
            Role one = roleService.lambdaQuery().eq(Role::getName, param.getRoleName()).one();
            if (one == null) {
                userService.removeById(user.getId());
                return ApiResult.error("没有找到该角色信息");
            }
            UserRole build = UserRole.builder().userId(user.getId()).roleId(one.getId()).build();
            userRoleService.save(build);
            return ApiResult.success("保存成功");
        }
        return ApiResult.error("保存失败");
    }

    @AuthCheck
    @ApiOperation("管理端获取用户列表")
    @PostMapping("/admin/list")
    public ApiResult getAdminUserList(@RequestBody AdminUserQueryParam adminUserQueryParam) {
        IPage<User> page = new Page<>(adminUserQueryParam.getCurrent(), adminUserQueryParam.getSize());
        IPage<User> pageInfo = userService.getAdminUserList(page, adminUserQueryParam);
        IPage<AdminUserDto> resultPage = new Page<>();
        BeanUtil.copyProperties(pageInfo, resultPage, "records");
        resultPage.setRecords(pageInfo.getRecords().stream().map(temp -> {
            UserDto userDto = userService.getUserDetailInfo(temp.getId());
            AdminUserDto adminUserDto = new AdminUserDto();
            BeanUtil.copyProperties(userDto, adminUserDto, "role");
            // 根据用户id查询角色
            adminUserDto.setRole(roleService.getRoleByUserId(adminUserDto.getId()));
            return adminUserDto;
        }).collect(Collectors.toList()));
        return ApiResult.success(resultPage);
    }

    @ApiOperation("修改用户密码")
    @PostMapping("/update/pass")
    @AuthCheck
    public ApiResult updatePass(@RequestBody @Validated UserPassParam userPassParam) {
        User user = LocalUser.getUser();
        // 首先判断原始密码是否正确
        User selectUser = userService.getById(user.getId());
        boolean matches = bCryptPasswordEncoder.matches(userPassParam.getOldPassword(), selectUser.getPassword());
        if (matches) {
            // 原始密码正确
            // 判断新密码和确认密码是否正确
            if (!StrUtil.equals(userPassParam.getNewPassword(), userPassParam.getConfirmPassword())) {
                return ApiResult.error("两次密码不一致");
            }
            // 修改密码
            selectUser.setPassword(bCryptPasswordEncoder.encode(userPassParam.getNewPassword()));
            boolean result = userService.updateById(selectUser);
            if (result) {
                return ApiResult.success("修改成功");
            }
        }
        return ApiResult.error("原始密码错误");
    }

    @ApiOperation("/根据生日计算星座和生肖")
    @AuthCheck
    @GetMapping("/calc/zodiac/{dateStr}")
    @Cacheable(cacheNames = "calc/zodiac", keyGenerator = RedisConst.REDIS_SJ_KEY_GENERATOR)
    public ApiResult calcZodiac(@PathVariable String dateStr) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime date = null;
        try {
            date = LocalDateTime.parse(dateStr, df);
        } catch (Exception e) {
            throw new CustomException("日期格式错误");
        }
        if (date == null) {
            throw new CustomException("日期格式错误");
        }
        return ApiResult.success(MapUtil.builder()
                .put("constellation", DateUtil.getZodiac(date.getMonthValue() - 1, date.getDayOfMonth()))
                .put("chineseZodiac", DateUtil.getChineseZodiac(date.getYear()))
                .build());
    }

    @AuthCheck
    @PostMapping("/update")
    @ApiOperation("修改用户个人信息")
    public ApiResult updateUserInfo(@RequestBody UserParam userParam) {
        User user = new User();
        BeanUtil.copyProperties(userParam, user);
        // 根据生日算出生肖，星座
        if (userParam.getBirthday() != null) {
            user.setConstellation(DateUtil.getZodiac(userParam.getBirthday().getMonthValue() - 1, userParam.getBirthday().getDayOfMonth()));
            user.setChineseZodiac(DateUtil.getChineseZodiac(userParam.getBirthday().getYear()));
        }
        User localUser = LocalUser.getUser();
        user.setUsername(localUser.getUsername());
        user.setId(localUser.getId());
        if (userService.updateById(user)) {
            user.setPassword(null);
            return ApiResult.success(user);
        }
        return ApiResult.error("修改失败");
    }

    @GetMapping("/article/data")
    @AuthCheck
    @ApiOperation("获取用户各个文章数量个数")
    @Cacheable(cacheNames = "article-num-data", keyGenerator = RedisConst.REDIS_SJ_KEY_GENERATOR)
    public ApiResult getUserArticleData() {
        User user = LocalUser.getUser();

        UserArticleDto userArticleInfo = UserArticleDto.builder()
                .id(user.getId())
                .answerNum(commentService.getUserAnswerCount(user.getId()))
                .attentionQuestionNum(collectService.getFixArticleCollectNum(user.getId(), Const.ARTICLE_QUESTION))
                .blogNum(articleService.getUserArticleNum(user.getId(), Const.ARTICLE_BLOG))
                .collectNum(collectService.getUserCollectNum(user.getId(), Const.ARTICLE))
                .ideaNum(articleService.getUserArticleNum(user.getId(), Const.ARTICLE_IDEA))
                .questionNum(articleService.getUserArticleNum(user.getId(), Const.ARTICLE_QUESTION))
                .footprintNum(footprintService.getUserFootprintNum(user.getId()))
                .build();

        List<DictDetail> dictDetailList = dictDetailService.lambdaQuery()
                .inSql(DictDetail::getDictId, "select id from dict where del = 0 and name = '" + Const.ARTICLE_NUM_DICT + "'")
                .orderBy(true, true, DictDetail::getSort).list();

        return ApiResult.success(dictDetailList.stream().map(detail -> {
            ArticleNumDict articleNumDict = new ArticleNumDict();
            BeanUtil.copyProperties(detail, articleNumDict, "articleNum");
            switch (articleNumDict.getValue()) {
                case Const.ANSWER_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getAnswerNum());
                    break;
                case Const.ATTENTION_QUESTION_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getAttentionQuestionNum());
                    break;
                case Const.BLOG_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getBlogNum());
                    break;
                case Const.COLLECT_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getCollectNum());
                    break;
                case Const.IDEA_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getIdeaNum());
                    break;
                case Const.QUESTION_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getQuestionNum());
                    break;
                case Const.FOOTPRINT_NUM:
                    articleNumDict.setArticleNum(userArticleInfo.getFootprintNum());
                    break;
                default:
                    break;
            }
            return articleNumDict;
        }).collect(Collectors.toList()));
    }

}
